#pragma once
#include "VulkanDescriptorSets.h"
#include "VulkanResource.h"
#include <functional>

#include "VulkanRenderTarget.h"
#include "VulkanShader.h"

namespace Alice
{
    class VulkanDevice;
	class VulkanShader;
    struct FDescriptorSetLayoutBinding
    {
        uint32 Binding;
        uint8 DescriptorType;
        uint8 StageFlags;

        void ReadFrom(const VkDescriptorSetLayoutBinding& InState);
        void WriteInto(VkDescriptorSetLayoutBinding& OutState) const;

        bool operator==(const FDescriptorSetLayoutBinding& In) const
        {
            return  Binding == In.Binding &&
                DescriptorType == In.DescriptorType &&
                StageFlags == In.StageFlags;
        }
    	uint32 GetSize() const
        {
	        return sizeof(*this);
        }
    };
	struct VulkanGraphicsPipelineObjectKey
	{
		VulkanGraphicsPipelineObjectKey(const void* inGraphicsPipelineDescription);
		bool operator==(const VulkanGraphicsPipelineObjectKey&inOther) const
		{
			return (mDataLen==inOther.mDataLen)&&memcmp(mData,inOther.mData,mDataLen)==0;
		}
		void*mData;
		uint32 mDataLen;
		uint32 mKey;
	};
    struct VulkanGraphicsPipelineDescription
    {
    	struct FBlendAttachment
    	{
    		bool bBlend;
    		uint8 ColorBlendOp;
    		uint8 SrcColorBlendFactor;
    		uint8 DstColorBlendFactor;
    		uint8 AlphaBlendOp;
    		uint8 SrcAlphaBlendFactor;
    		uint8 DstAlphaBlendFactor;
    		uint8 ColorWriteMask;

    		void ReadFrom(const VkPipelineColorBlendAttachmentState& InState);
    		void WriteInto(VkPipelineColorBlendAttachmentState& OutState) const;

    		bool operator==(const FBlendAttachment& In) const
    		{
    			return bBlend == In.bBlend &&
					ColorBlendOp == In.ColorBlendOp &&
					SrcColorBlendFactor == In.SrcColorBlendFactor &&
					DstColorBlendFactor == In.DstColorBlendFactor &&
					AlphaBlendOp == In.AlphaBlendOp &&
					SrcAlphaBlendFactor == In.SrcAlphaBlendFactor &&
					DstAlphaBlendFactor == In.DstAlphaBlendFactor &&
					ColorWriteMask == In.ColorWriteMask;
    		}
    		uint32 GetSize() const
    		{
    			return sizeof(*this);
    		}
    	};
    	struct FVertexBinding
    	{
    		uint32 Stride;
    		uint16 Binding;
    		uint16 InputRate;

    		void ReadFrom(const VkVertexInputBindingDescription& InState);
    		void WriteInto(VkVertexInputBindingDescription& OutState) const;

    		bool operator==(const FVertexBinding& In) const
    		{
    			return Stride == In.Stride &&
					Binding == In.Binding &&
					InputRate == In.InputRate;
    		}
    		uint32 GetSize() const
    		{
    			return sizeof(*this);
    		}
    	};
    	struct FVertexAttribute
    	{
    		uint32 Location;
    		uint32 Binding;
    		uint32 Format;
    		uint32 Offset;

    		void ReadFrom(const VkVertexInputAttributeDescription& InState);
    		void WriteInto(VkVertexInputAttributeDescription& OutState) const;

    		bool operator==(const FVertexAttribute& In) const
    		{
    			return Location == In.Location &&
					Binding == In.Binding &&
					Format == In.Format &&
					Offset == In.Offset;
    		}
    		uint32 GetSize() const
    		{
    			return sizeof(*this);
    		}
    	};
    	struct FRasterizer
    	{
    		uint8 PolygonMode;
    		uint8 CullMode;
    		float DepthBiasSlopeScale;
    		float DepthBiasConstantFactor;

    		void ReadFrom(const VkPipelineRasterizationStateCreateInfo& InState);
    		void WriteInto(VkPipelineRasterizationStateCreateInfo& OutState) const;

    		bool operator==(const FRasterizer& In) const
    		{
    			return PolygonMode == In.PolygonMode &&
					CullMode == In.CullMode &&
					DepthBiasSlopeScale == In.DepthBiasSlopeScale &&
					DepthBiasConstantFactor == In.DepthBiasConstantFactor;
    		}
    		uint32 GetSize() const
    		{
    			return sizeof(*this);
    		}
    	};
    	struct FDepthStencil
    	{
    		uint8 DepthCompareOp;
    		bool bDepthTestEnable;
    		bool bDepthWriteEnable;
    		bool bStencilTestEnable;
    		bool bDepthBoundsTestEnable;
    		uint8 FrontFailOp;
    		uint8 FrontPassOp;
    		uint8 FrontDepthFailOp;
    		uint8 FrontCompareOp;
    		uint32 FrontCompareMask;
    		uint32 FrontWriteMask;
    		uint32 FrontReference;
    		uint8 BackFailOp;
    		uint8 BackPassOp;
    		uint8 BackDepthFailOp;
    		uint8 BackCompareOp;
    		uint32 BackCompareMask;
    		uint32 BackWriteMask;
    		uint32 BackReference;

    		void ReadFrom(const VkPipelineDepthStencilStateCreateInfo& InState);
    		void WriteInto(VkPipelineDepthStencilStateCreateInfo& OutState) const;

    		bool operator==(const FDepthStencil& In) const
    		{
    			return DepthCompareOp == In.DepthCompareOp &&
					bDepthTestEnable == In.bDepthTestEnable &&
					bDepthWriteEnable == In.bDepthWriteEnable &&
					bDepthBoundsTestEnable == In.bDepthBoundsTestEnable &&
					bStencilTestEnable == In.bStencilTestEnable &&
					FrontFailOp == In.FrontFailOp &&
					FrontPassOp == In.FrontPassOp &&
					FrontDepthFailOp == In.FrontDepthFailOp &&
					FrontCompareOp == In.FrontCompareOp &&
					FrontCompareMask == In.FrontCompareMask &&
					FrontWriteMask == In.FrontWriteMask &&
					FrontReference == In.FrontReference &&
					BackFailOp == In.BackFailOp &&
					BackPassOp == In.BackPassOp &&
					BackDepthFailOp == In.BackDepthFailOp &&
					BackCompareOp == In.BackCompareOp &&
					BackCompareMask == In.BackCompareMask &&
					BackWriteMask == In.BackWriteMask &&
					BackReference == In.BackReference;
    		}
    		uint32 GetSize() const
    		{
    			return sizeof(*this);
    		}
    	};

		struct FRenderTargets
		{
			struct FAttachmentRef
			{
				uint32 Attachment;
				uint64 Layout;

				void ReadFrom(const VkAttachmentReference& InState);
				void WriteInto(VkAttachmentReference& OutState) const;
				bool operator == (const FAttachmentRef& In) const
				{
					return Attachment == In.Attachment && Layout == In.Layout;
				}
				uint32 GetSize() const
				{
					return sizeof(*this);
				}
			};

			struct FStencilAttachmentRef
			{
				uint64 Layout;

				void ReadFrom(const VkAttachmentReferenceStencilLayout& InState);
				void WriteInto(VkAttachmentReferenceStencilLayout& OutState) const;
				bool operator == (const FStencilAttachmentRef& In) const
				{
					return Layout == In.Layout;
				}
				uint32 GetSize() const
				{
					return sizeof(*this);
				}
			};

			struct FAttachmentDesc
			{
				uint32 Format;
				uint8 Flags;
				uint8 Samples;
				uint8 LoadOp;
				uint8 StoreOp;
				uint8 StencilLoadOp;
				uint8 StencilStoreOp;
				uint64 InitialLayout;
				uint64 FinalLayout;

				bool operator==(const FAttachmentDesc& In) const
				{
					return Format == In.Format &&
						Flags == In.Flags &&
						Samples == In.Samples &&
						LoadOp == In.LoadOp &&
						StoreOp == In.StoreOp &&
						StencilLoadOp == In.StencilLoadOp &&
						StencilStoreOp == In.StencilStoreOp &&
						InitialLayout == In.InitialLayout &&
						FinalLayout == In.FinalLayout;
				}

				void ReadFrom(const VkAttachmentDescription &InState);
				void WriteInto(VkAttachmentDescription& OutState) const;
				uint32 GetSize() const
				{
					return sizeof(*this);
				}
			};

			struct FStencilAttachmentDesc
			{
				uint64 InitialLayout;
				uint64 FinalLayout;

				bool operator==(const FStencilAttachmentDesc& In) const
				{
					return InitialLayout == In.InitialLayout &&
						FinalLayout == In.FinalLayout;
				}

				void ReadFrom(const VkAttachmentDescriptionStencilLayout& InState);
				void WriteInto(VkAttachmentDescriptionStencilLayout& OutState) const;
				uint32 GetSize() const
				{
					return sizeof(*this);
				}
			};


			std::vector<FAttachmentRef> ColorAttachments;
			std::vector<FAttachmentRef> ResolveAttachments;
			std::vector<FAttachmentDesc> Descriptions;
			FAttachmentRef Depth;
			FStencilAttachmentRef Stencil;
			FAttachmentRef FragmentDensity;
			FStencilAttachmentDesc StencilDescription;
			uint8 NumAttachments;
			uint8 NumColorAttachments;
			uint8 bHasDepthStencil;
			uint8 bHasResolveAttachments;
			uint8 bHasDepthStencilResolve;
			uint8 bHasFragmentDensityAttachment;
			uint8 NumUsedClearValues;
			uint32 RenderPassCompatibleHash;
			Vector3f Extent3D;

			void ReadFrom(const VulkanRenderTargetLayout &InState);
			void WriteInto(VulkanRenderTargetLayout& OutState) const;

			bool operator==(const FRenderTargets& In) const
			{
				return ColorAttachments == In.ColorAttachments &&
					ResolveAttachments == In.ResolveAttachments &&
					Depth == In.Depth &&
					Stencil == In.Stencil &&
					FragmentDensity == In.FragmentDensity &&
					Descriptions == In.Descriptions &&
					StencilDescription == In.StencilDescription &&
					NumAttachments == In.NumAttachments &&
					NumColorAttachments == In.NumColorAttachments &&
					bHasDepthStencil == In.bHasDepthStencil &&
					bHasResolveAttachments == In.bHasResolveAttachments &&
					bHasDepthStencilResolve == In.bHasDepthStencilResolve &&
					bHasFragmentDensityAttachment == In.bHasFragmentDensityAttachment &&
					NumUsedClearValues == In.NumUsedClearValues &&
					RenderPassCompatibleHash == In.RenderPassCompatibleHash &&
					Extent3D == In.Extent3D;
			}
			uint32 GetSize() const
			{
				uint32 dataLen=0;
				for(uint32 i=0;i<ColorAttachments.size();i++)
				{
					dataLen+=ColorAttachments[i].GetSize();
				}
				for(uint32 i=0;i<ResolveAttachments.size();i++)
				{
					dataLen+=ResolveAttachments[i].GetSize();
				}
				for(uint32 i=0;i<Descriptions.size();i++)
				{
					dataLen+=Descriptions[i].GetSize();
				}
				return dataLen + 11 + sizeof(Vector3f) + Depth.GetSize() + Stencil.GetSize() + FragmentDensity.GetSize() + StencilDescription.GetSize();
			}
		};
		uint32 VertexInputKey;
		uint16 RasterizationSamples;
		uint16 ControlPoints;
		uint32 Topology;
		std::vector<FBlendAttachment> ColorAttachmentStates;
		std::vector<std::vector<FDescriptorSetLayoutBinding>> DescriptorSetLayoutBindings;
		std::vector<FVertexBinding> VertexBindings;
		std::vector<FVertexAttribute> VertexAttributes;
		FRasterizer Rasterizer;
		FDepthStencil DepthStencil;
		uint64 ShaderKeys[EShaderStage::SS_NumGraphicsStages];
		uint64 ShaderKeyShared;
		FRenderTargets RenderTargets;
		uint8 SubpassIndex;
		uint8 UseAlphaToCoverage;
		EVRSShadingRate ShadingRate = EVRSShadingRate::VRSSR_1x1;
		EVRSRateCombiner Combiner = EVRSRateCombiner::VRSRB_Passthrough;

		bool operator==(const VulkanGraphicsPipelineDescription& In) const
		{
			if (VertexInputKey != In.VertexInputKey)
			{
				return false;
			}

			if (RasterizationSamples != In.RasterizationSamples)
			{
				return false;
			}

			if (Topology != In.Topology)
			{
				return false;
			}

			if (ColorAttachmentStates != In.ColorAttachmentStates)
			{
				return false;
			}

			if (DescriptorSetLayoutBindings != In.DescriptorSetLayoutBindings)
			{
				return false;
			}

			if (!(Rasterizer == In.Rasterizer))
			{
				return false;
			}

			if (!(DepthStencil == In.DepthStencil))
			{
				return false;
			}

			if (!(SubpassIndex == In.SubpassIndex))
			{
				return false;
			}

			if (!(UseAlphaToCoverage == In.UseAlphaToCoverage))
			{
				return false;
			}
			if (0 != memcmp(ShaderKeys, In.ShaderKeys, sizeof(ShaderKeys)))
			{
				return false;
			}
			if (!(RenderTargets == In.RenderTargets))
			{
				return false;
			}

			if (VertexBindings != In.VertexBindings)
			{
				return false;
			}

			if (VertexAttributes != In.VertexAttributes)
			{
				return false;
			}

			if (ShadingRate != In.ShadingRate)
			{
				return false;
			}
			
			if (Combiner != In.Combiner)
			{
				return false;
			}
			return true;
		}
		VulkanGraphicsPipelineObjectKey CreateKey() const;
	};
    class VulkanLayout
    {
    public:
        VulkanDevice*mDevice;
        const bool mbIsGraphicsLayout;
        const bool mbUseBindlessResources;
        FVulkanDescriptorSetsLayout mDescriptorSetsLayout;
    	VkPipelineLayout			mPipelineLayout;
        VulkanLayout(VulkanDevice*inDevice,bool inIsGraphicsLayout,bool inUseBindlessResources);
    	inline uint32 GetDescriptorSetLayoutHash() const
    	{
    		return mDescriptorSetsLayout.GetHash();
    	}
    };
    class FVulkanPipeline
    {
    public:
        FVulkanPipeline(VulkanDevice* InDevice);
        VkPipeline mPipeline;
    };
	class FVulkanComputePipeline:public FVulkanPipeline
	{
	public:
	};
	class FVulkanCGIGraphicPipelineState:public CGIGraphicsPipelineState
	{
	public:
		VulkanDevice*mDevice;
		VkPipeline mPipeline;
		VulkanLayout* mLayout;
		VulkanRenderPass* mRenderPass;
		VulkanGraphicsPipelineDescription mVulkanGraphicsPipelineDescription;
		FVulkanCGIGraphicPipelineState(VulkanDevice* inDevice, const FGraphicsPipelineStateInitializer& PSOInitializer, const VulkanGraphicsPipelineDescription& Desc);
		CGIGraphicsShader* GetShader(EShaderFrequency Frequency) const;
		void Bind(VkCommandBuffer CmdBuffer) const;
	};
	class FVulkanPipelineStateCacheManager
	{
	public:
		VulkanDevice* mDevice;
		//std::unordered_map<FVulkanDescriptorSetsLayoutInfo,VulkanLayout*> mLayouts;
		FVulkanPipelineStateCacheManager(VulkanDevice*inDevice);
		CGIGraphicsPipelineState* RHICreateGraphicsPipelineState(const FGraphicsPipelineStateInitializer& Initializer);
		void CreateGraphicsEntry(const FGraphicsPipelineStateInitializer& PSOInitializer, FVulkanDescriptorSetsLayoutInfo& DescriptorSetLayoutInfo, VulkanGraphicsPipelineDescription* Desc);
		bool CreateGraphicsPipelineFromEntry(FVulkanCGIGraphicPipelineState* PSO, VulkanShader* Shaders[EShaderStage::SS_NumGraphicsStages], FGraphicsPipelineStateInitializer::EPSOPrecacheCompileType PSOCompileType);
		VkResult CreateVKPipeline(FVulkanCGIGraphicPipelineState* PSO, VulkanShader* Shaders[EShaderStage::SS_NumGraphicsStages], const VkGraphicsPipelineCreateInfo& PipelineInfo, FGraphicsPipelineStateInitializer::EPSOPrecacheCompileType PSOCompileType);
		
	};
}

template<>
struct std::hash<Alice::VulkanGraphicsPipelineObjectKey>
{
	size_t operator()(const Alice::VulkanGraphicsPipelineObjectKey& inVulkanGraphicsPipelineObjectKey) const
	{
		return inVulkanGraphicsPipelineObjectKey.mKey;
	}
};